
<template>
  <!-- 这是一个Vue 的<transition>组件，用于在元素或组件的插入、更新或移除时应用过渡效果
  ：name="transitionName"是一个绑定，表示过渡效果的名称将从组建的transitionName属性中获取 -->
  <transition :name="transitionName">
    <div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
      <svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px"><path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" /></svg>
    </div>
  </transition>
  <!-- 这段代码创建了一个可以通过点击返回打破页面顶部的按钮，并使用了SVG图标作为视觉标识
  同时，使用了Vue的过渡效果，条件显示和时间监听等功能来增强交互性和用户体验 -->
</template>

<script>
export default {
  name: 'BackToTop',
  props: { // 定义组件的属性，控制返回顶部按钮的外观和行为，通过设置这些属性，可以定制化返回顶部按钮的样式和功能。
    visibilityHeight: { // 纵向滑动多远距离出现滚动条
      type: Number,
      default: 400
    },
    backPosition: { // 返回顶部时，滚动到哪里（距离顶部的距离）
      type: Number,
      default: 0
    },
    customStyle: { // 按钮样式
      type: Object,
      default: function() {
        return {
          right: '50px',
          bottom: '50px',
          width: '40px',
          height: '40px',
          'border-radius': '4px',
          'line-height': '45px',
          background: '#e7eaf1'
        }
      }
    },
    transitionName: { // 返回顶部按钮的过渡动画名称
      type: String,
      default: 'fade'// 表示按钮的出现和消失会使用淡入淡出的过渡效果，还有滑动、缩放等
    }
  },
  data() { // 定义了组件的数据
    return {
      visible: true, // 默认按钮不出现,我将false改成true了
      interval: null, // 定义定时器
      isMoving: false
    }
  },
  // vue组件的生命周期钩子函数
  mounted() { // 用于在组件挂载到DOM后添加滚动事件监听器，当页面进行滚动时，会触发handleScroll方法。在这个钩子函数中，通过监听滚动事件，可以在页面滚动时执行一些特定的逻辑操作
    window.addEventListener('scroll', this.handleScroll)// 一般情况下，handleScroll方法会被定义在组件的methods选项中。
  },
  beforeDestroy() { // 在组件销毁前移除该事件监听器
    window.removeEventListener('scroll', this.handleScroll)// 防止内存泄露，因为如果一个组件被销毁了但是仍然保留着事件监听器，就会造成内存泄露问题，因为这个组件不再被使用，但是事件监听器却仍然存在，占用着内存
    if (this.interval) { // 确保在组件销毁之前，清除所有可能存在的定时器，即停止定时器的执行，以防止他们继续消耗系统资源后者导致一些意外的行为发生
      clearInterval(this.interval)
    }
  },
  methods: {
    handleScroll() { // 当页面滚动时，该方法会被调用。根据页面滚动的位置更新this.visible的值，如果页面滚动超过了this.visibilityHeight，则将this.visible设置为true，否则设置为false
      this.visible = window.pageYOffset > this.visibilityHeight
    },
    backToTop() { // 当点击返回顶部按钮时，该方法被调用
      if (this.isMoving) return// 首先会检查是否正在进行滚动操作，如果是则直接返回
      const start = window.pageYOffset
      let i = 0
      this.isMoving = true
      this.interval = setInterval(() => {
        const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
        if (next <= this.backPosition) {
          window.scrollTo(0, this.backPosition)
          clearInterval(this.interval)
          this.isMoving = false
        } else {
          window.scrollTo(0, next)
        }
        i++
      }, 16.7)
    }, // 实现了一个平滑返回顶部的功能，通过不断计算下一个滚动位置，并使用缓动函数进行滚动操作，直到页面滚动到顶部位置为止。
    easeInOutQuad(t, b, c, d) { // 缓动函数t 当前时间，b 起始位置，即滚动操作开始时位置，c 变化量，及目标位置与起始位置之间的差值，d 持续时间，滚动操作持续时间
      if ((t /= d / 2) < 1) return c / 2 * t * t + b
      return -c / 2 * (--t * (t - 2) - 1) + b
    }
  }
}
</script>

<style scoped>
.back-to-ceiling {
  position: fixed;
  display: inline-block;
  text-align: center;
  cursor: pointer;
}

.back-to-ceiling:hover {
  background: #d5dbe7;
}
/* 两个类选择器用于定义过渡效果的持续时间和属性 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity .5s;/* 表示过渡效果持续0.5秒 */
}
/* 定义过渡效果的初始状态和结束状态 */
.fade-enter,
.fade-leave-to {
  opacity: 0/* 设置透明度为0，表示元素初始时是完全透明不可见的 */
}

.back-to-ceiling .Icon {
  fill: #9aaabf;
  background: none;
}
</style>
